package cn.huaguo.infrastructure.persistent.repository;

import cn.huaguo.domain.order.model.entity.OrderEntity;
import cn.huaguo.domain.order.model.valobj.OrderStatusVO;
import cn.huaguo.domain.task.model.aggragate.PublishTradeTaskAggregate;
import cn.huaguo.domain.task.model.entity.PublishTaskEntity;
import cn.huaguo.domain.task.model.entity.TaskEntity;
import cn.huaguo.domain.task.model.valobj.TaskPageQueryVO;
import cn.huaguo.domain.task.model.valobj.TaskStatusVO;
import cn.huaguo.domain.task.repository.ITaskRepository;
import cn.huaguo.domain.wallet.model.entity.WalletEntity;
import cn.huaguo.domain.wallet.model.valobj.AdjustTypeVO;
import cn.huaguo.infrastructure.persistent.dao.IOrderDao;
import cn.huaguo.infrastructure.persistent.dao.ITaskDao;
import cn.huaguo.infrastructure.persistent.dao.IWalletAdjustFlowDao;
import cn.huaguo.infrastructure.persistent.dao.IWalletDao;
import cn.huaguo.infrastructure.persistent.po.Order;
import cn.huaguo.infrastructure.persistent.po.Task;
import cn.huaguo.infrastructure.persistent.po.Wallet;
import cn.huaguo.infrastructure.persistent.po.WalletAdjustFlow;
import cn.huaguo.infrastructure.persistent.redis.IRedisService;
import cn.huaguo.types.common.Constants;
import cn.huaguo.types.enums.ResponseCode;
import cn.huaguo.types.exception.AppException;
import cn.huaguo.types.model.PageResult;
import com.alibaba.fastjson2.JSON;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.redisson.api.RBlockingQueue;
import org.redisson.api.RDelayedQueue;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @Description 任务仓储接口实现类
 * @Author wangQian
 * @Date 2024/5/13
 */
@Slf4j
@Repository
public class TaskRepository implements ITaskRepository {
    @Resource
    private IRedisService redisService;
    @Resource
    private ITaskDao taskDao;
    @Resource
    private IOrderDao orderDao;
    @Resource
    private IWalletDao walletDao;
    @Resource
    private IWalletAdjustFlowDao walletAdjustFlowDao;
    @Resource
    private TransactionTemplate transactionTemplate;

    /**
     * 新增任务
     *
     * @param publishTradeTaskAggregate
     */
    @Override
    public PublishTaskEntity newTradeTask(PublishTradeTaskAggregate publishTradeTaskAggregate) {
        PublishTaskEntity publishTaskEntity = new PublishTaskEntity();
        publishTaskEntity.setUserId(publishTradeTaskAggregate.getUserId());
        TaskEntity taskEntity = publishTradeTaskAggregate.getTaskEntity();
        OrderEntity orderEntity = publishTradeTaskAggregate.getOrderEntity();

        // 构建任务持久化对象
        Task task = new Task();
        task.setTaskId(taskEntity.getTaskId());
        task.setTaskContent(taskEntity.getTaskContent());
        task.setTaskReward(taskEntity.getTaskReward());
        task.setTaskPhone(taskEntity.getTaskPhone());
        task.setDeadline(taskEntity.getDeadline());
        task.setTaskAddress(taskEntity.getTaskAddress());
        task.setTaskType(taskEntity.getTaskType());
        task.setTaskStatus(TaskStatusVO.CREATE.getCode());
        task.setTaskStock(1);
        task.setCreateUserId(taskEntity.getCreateUserId());
        task.setOrderId(orderEntity.getOrderId());
        task.setCreateTime(LocalDateTime.now());
        task.setUpdateTime(LocalDateTime.now());

        // 构建订单持久化对象
        Order order = new Order();
        order.setTaskId(taskEntity.getTaskId());
        order.setTaskContent(taskEntity.getTaskContent());
        order.setOrderId(orderEntity.getOrderId());
        order.setCreateUserId(orderEntity.getCreateUserId());
        order.setOrderStatus(OrderStatusVO.CREATE.getCode());
        order.setOrderAmount(orderEntity.getOrderAmount());
        order.setOrderTime(LocalDateTime.now());
        order.setCreateTime(LocalDateTime.now());
        order.setUpdateTime(LocalDateTime.now());

        transactionTemplate.execute(status -> {
            try {
                // 新增任务
                taskDao.addTask(task);
                // 新增订单
                orderDao.addOrder(order);
                publishTaskEntity.setTaskId(task.getTaskId());
                publishTaskEntity.setOrderId(order.getOrderId());
                return 1;
            } catch (DuplicateKeyException e) {
                status.setRollbackOnly();
                log.error("写入任务订单，唯一索引冲突 userId: {} taskId: {} orderId: {}", taskEntity.getCreateUserId(), taskEntity.getTaskId(), orderEntity.getOrderId(), e);
                throw new AppException(ResponseCode.INDEX_DUP.getCode());
            }
        });
        // 缓存任务库存
        redisService.setAtomicLong(Constants.RedisKey.TASK_STOCK_KEY + task.getTaskId(), task.getTaskStock());
        return publishTaskEntity;
    }

    /**
     * 条件查询任务列表
     *
     * @param taskPageQueryVO
     * @return
     */
    @Override
    public PageResult queryTaskList(TaskPageQueryVO taskPageQueryVO, String createUserId) {

        PageResult<TaskEntity> pageResult = new PageResult<>();

        // 开启分页
        PageHelper.startPage(taskPageQueryVO.getPageNum(), taskPageQueryVO.getPageSize());

        List<Task> taskList = taskDao.queryTaskList(taskPageQueryVO.getSearchKey(), taskPageQueryVO.getCampusName(),
                taskPageQueryVO.getTaskType(), createUserId, TaskStatusVO.PUBLISH.getCode());

        // 缓存任务库存
        for (Task task : taskList) {
            redisService.setAtomicLong(Constants.RedisKey.TASK_STOCK_KEY + task.getTaskId(), task.getTaskStock());
        }

        Page<TaskEntity> p = (Page) taskList;

        //把数据填充到PageBean对象中
        pageResult.setTotal(p.getTotal());
        pageResult.setItems(p.getResult());

        return pageResult;
    }

    /**
     * 根据id查询任务
     *
     * @param taskId
     * @return
     */
    @Override
    public TaskEntity getTaskById(String taskId) {
        Task taskPO = taskDao.getTaskById(taskId);
        TaskEntity task = TaskEntity.builder()
                .taskId(taskPO.getTaskId())
                .taskContent(taskPO.getTaskContent())
                .taskReward(taskPO.getTaskReward())
                .taskPhone(taskPO.getTaskPhone())
                .deadline(taskPO.getDeadline())
                .taskAddress(taskPO.getTaskAddress())
                .taskType(taskPO.getTaskType())
                .taskStatus(taskPO.getTaskStatus())
                .createUserId(taskPO.getCreateUserId())
                .takeUserId(taskPO.getTakeUserId())
                .orderId(taskPO.getOrderId())
                .createTime(taskPO.getCreateTime())
                .build();

        // 缓存任务库存
        redisService.setAtomicLong(Constants.RedisKey.TASK_STOCK_KEY + taskPO.getTaskId(), taskPO.getTaskStock());
        return task;
    }

    /**
     * 更新任务 包含更新状态
     *
     * @param taskEntity
     * @return
     */
    @Override
    public Boolean updateTask(TaskEntity taskEntity) {
        Task task = Task.builder()
                .taskId(taskEntity.getTaskId())
                .taskContent(taskEntity.getTaskContent())
                .taskReward(taskEntity.getTaskReward())
                .taskPhone(taskEntity.getTaskPhone())
                .deadline(taskEntity.getDeadline())
                .taskAddress(taskEntity.getTaskAddress())
                .taskType(taskEntity.getTaskType())
                .taskStatus(taskEntity.getTaskStatus())
                .createUserId(taskEntity.getCreateUserId())
                .orderId(taskEntity.getOrderId())
                .takeUserId(taskEntity.getTakeUserId())
                .updateTime(LocalDateTime.now())
                .build();

        if (TaskStatusVO.FINISH.getCode().equals(taskEntity.getTaskStatus())) {
            task.setFinishTime(LocalDateTime.now());
        }
        return taskDao.updateTask(task) > 0;
    }

    /**
     * 根据id删除任务
     *
     * @param taskId
     * @return
     */
    @Override
    public void deleteTaskById(String taskId) {
        taskDao.deleteTaskById(taskId);
    }

    @Override
    public Boolean subtractStock(String taskId) {
        String cacheKey = Constants.RedisKey.TASK_STOCK_KEY + taskId;

        long surplus = redisService.decr(cacheKey);
        if (surplus < 0) {
            // 库存小于0，恢复为0个
            redisService.setValue(cacheKey, 0);
            return false;
        }
        // 1. 按照cacheKey decr 后的值，如 99、98、97 和 key 组成为库存锁的key进行使用。
        // 2. 加锁为了兜底，如果后续有恢复库存，手动处理等，也不会超卖。因为所有的可用库存key，都被加锁了。
        String lockKey = cacheKey + Constants.UNDERLINE + surplus;
        Boolean lock = redisService.setNx(lockKey);
        if (!lock) {
            log.info("库存加锁失败 {}", lockKey);
        }
        return lock;
    }

    @Override
    public void sendConsumerQueue(String taskId) {
        String cacheKey = Constants.RedisKey.TASK_STOCK_QUERY_KEY;
        // 创建队列信息
        RBlockingQueue<String> blockingQueue = redisService.getBlockingQueue(cacheKey);
        // 将队列信息放入延迟队列
        RDelayedQueue<String> delayedQueue = redisService.getDelayedQueue(blockingQueue);
        // 设置消费时延
        delayedQueue.offer(taskId, 1, TimeUnit.SECONDS);
    }

    public String takeQueueValue() {
        String cacheKey = Constants.RedisKey.TASK_STOCK_QUERY_KEY;
        RBlockingQueue<String> destinationQueue = redisService.getBlockingQueue(cacheKey);
        return destinationQueue.poll();
    }

    /**
     * 更新任务库存
     *
     * @param taskId
     */
    @Override
    public void updateTaskStock(String taskId) {
        taskDao.updateTaskStock(taskId);
    }

    /**
     * 支付成功后回调 更新任务及订单状态
     *
     * @param publishTaskEntity
     */
    @Override
    public void updateTaskOrderStatus(PublishTaskEntity publishTaskEntity) {
        Task task = Task.builder()
                .taskId(publishTaskEntity.getTaskId())
                .taskStatus(TaskStatusVO.PUBLISH.getCode())
                .updateTime(LocalDateTime.now())
                .build();

        Order order = Order.builder()
                .orderId(publishTaskEntity.getOrderId())
                .orderStatus(OrderStatusVO.PAY_SUCCESS.getCode())
                .updateTime(LocalDateTime.now())
                .build();

        transactionTemplate.execute(status -> {
            try {
                taskDao.updateTask(task);
                orderDao.updateOrderStatus(order.getOrderId(), order.getOrderStatus());
                return 1;
            } catch (Exception e) {
                status.setRollbackOnly();
                log.error("支付成功-更新任务及订单状态失败 value: {}, info: {}", JSON.toJSONString(publishTaskEntity), e.getMessage());
                throw new RuntimeException(e);
            }
        });
    }

    /**
     * 发布无交易类型任务
     *
     * @param taskEntity
     */
    @Override
    public void publishNoTradeTask(TaskEntity taskEntity) {
        Task task = Task.builder()
                .taskId(taskEntity.getTaskId())
                .taskContent(taskEntity.getTaskContent())
                .taskReward(taskEntity.getTaskReward())
                .taskPhone(taskEntity.getTaskPhone())
                .deadline(taskEntity.getDeadline())
                .taskAddress(taskEntity.getTaskAddress())
                .taskType(taskEntity.getTaskType())
                .taskStatus(taskEntity.getTaskStatus())
                .taskStock(1)
                .createUserId(taskEntity.getCreateUserId())
                .createTime(LocalDateTime.now())
                .updateTime(LocalDateTime.now())
                .build();
        taskDao.addTask(task);
    }

    /**
     * 确认任务完成
     *
     * @param taskId
     * @param orderEntity
     * @param walletEntity
     */
    @Override
    public void confirmFinishTask(String taskId, OrderEntity orderEntity, WalletEntity walletEntity) {
        Wallet wallet = new Wallet();
        wallet.setBalance(walletEntity.getBalance());
        wallet.setUserId(walletEntity.getUserId());

        Task task = Task.builder()
                .taskId(taskId)
                .taskStatus(TaskStatusVO.FINISH.getCode())
                .updateTime(LocalDateTime.now())
                .finishTime(LocalDateTime.now())
                .build();

        WalletAdjustFlow walletAdjustFlow = WalletAdjustFlow.builder()
                .serialNumber(RandomStringUtils.randomNumeric(24))
                .userId(walletEntity.getUserId())
                .adjustAmount(orderEntity.getOrderAmount())
                .totalAmount(walletDao.getWalletInfo(walletEntity.getUserId()).getBalance())
                .adjustType(AdjustTypeVO.TASK_FINISH.getCode())
                .orderId(orderEntity.getOrderId())
                .createTime(LocalDateTime.now())
                .build();

        transactionTemplate.execute(status -> {
            try {
                int update = walletDao.updateWalletBalance(wallet);
                if (update > 0) {
                    taskDao.updateTask(task);
                    orderDao.updateOrderStatus(orderEntity.getOrderId(), OrderStatusVO.FINISH.getCode());
                    walletAdjustFlowDao.insertFlow(walletAdjustFlow);
                }
                return 1;
            } catch (Exception e) {
                status.setRollbackOnly();
                log.error("确认任务完成-更新订单任务、余额失败: taskId={}, orderId={}", taskId, orderEntity.getOrderId(), e.getMessage());
                throw new RuntimeException(e);
            }

        });
    }

    /**
     * 取消任务
     *
     * @param taskEntity
     * @param orderEntity
     */
    @Override
    public void cancelTask(TaskEntity taskEntity, OrderEntity orderEntity) {
        Task task = Task.builder()
                .taskId(taskEntity.getTaskId())
                .taskStatus(TaskStatusVO.REFUND.getCode())
                .updateTime(LocalDateTime.now())
                .finishTime(LocalDateTime.now())
                .build();
        Order order = Order.builder()
                .orderId(orderEntity.getOrderId())
                .orderStatus(OrderStatusVO.REFUND.getCode())
                .updateTime(LocalDateTime.now())
                .build();
        transactionTemplate.execute(status -> {
            try {
                taskDao.updateTask(task);
                orderDao.updateOrderStatus(order.getOrderId(), order.getOrderStatus());
                return 1;
            } catch (Exception e) {
                status.setRollbackOnly();
                log.error("任务取消-更新订单及任务状态失败 taskId={}, orderId={}", taskEntity.getTaskId(), orderEntity.getOrderId(), e.getMessage());
                throw new RuntimeException(e);
            }
        });

    }
}
